using System;
using System.Configuration;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data;
using System.Data.OleDb;
using System.Data.SqlClient;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.Management;
using System.Net;
using System.Net.Mail;
using System.Net.Mime;
using System.IO;
using System.IO.Compression;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Text;
using System.Text.RegularExpressions;
using System.Timers;
using System.Web;
using System.Web.Mail;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml;

namespace Halide
{
	/// <summary>
	/// The Storage class contains methods and properties for performing or evaluating disk and file operations.
	/// </summary>
	sealed public class Storage
	{
		private static string _version = "2008.02.26A";

		/// <summary>
		/// Return the version number of the class; read-only.
		/// </summary>
		/// <value>
		/// A string with the version number of the class.
		/// </value>
		public static string Version
		{
			get
			{
				return _version;
			}
		}

		/// <summary>
		/// No constructor is necessary.
		/// </summary>
        private Storage()
	    {
	    }

		/// <summary>
		/// The PathOptions enumeration is used to specify file attributes, like "hidden", "system", et al.
		/// </summary>
		[Flags]
		public enum PathOptions : byte
		{
			/// <summary>
			/// Normal files.
			/// </summary>
			Path=1,
			
			/// <summary>
			/// Hidden files.
			/// </summary>
			Hidden=2,
			
			/// <summary>
			/// System files.
			/// </summary>
			System=4
		}

	    /// <summary>
	    /// Deletes a directory, and any files and subdirectories it contains.
		/// Directory passed is in standard web form (eg. "/temp").
	    /// </summary>
		/// <example>
		/// <code>
		/// string result = Halide.Storage.DeleteDirectory("/temp/uploads");
		/// </code>
		/// </example>
	    /// <param name="directory">Directory to delete (as "/temp"). Passing an empty string will delete the current working directory!</param>
		/// <returns>Empty string on success, or a string with an error message.</returns>
	    public static string DeleteDirectory(string directory)
	    {
			if(Halide.StringJuggler.FixNull(directory).ToString().Length < 1)
			{
				return ("You did not specify a directory to delete.");
			}
			else
			{
				try
				{
					string webdir = HttpContext.Current.Server.MapPath(directory);

					string dirObject = String.Format("win32_Directory.Name='{0}'", webdir);

					using (ManagementObject dir = new ManagementObject(dirObject))
					{
						dir.Get();
						ManagementBaseObject outParams = dir.InvokeMethod("Delete", null, null);
					}
				}

				catch(DirectoryNotFoundException)
				{
					return ("The directory \"" + directory + "\" does not exist.");
				}

				catch(PathTooLongException)
				{
					return ("The directory \"" + directory.ToString().Substring(1, 80) + "...\" is too long and exceeds the system-defined maximum length for a directory name.");
				}

				catch(Exception e)
				{
					return ("Cannot delete the directory due to the following exception:<pre>\r\n" + e + "<pre>");
				}

				return (string.Empty);
			}
	    }

	    /// <summary>
	    /// Reads the contents of a text file.
	    /// </summary>
		/// <example>
		/// <code>
		/// string fileContents = Halide.Storage.ReadFile("/content/file.txt");
		/// </code>
		/// </example>
	    /// <param name="filename">Web-formatted path and file name to read.</param>
		/// <returns>String with file contents.</returns>
	    public static string ReadFile(string filename)
	    {
	        string inn = "";

			try
			{
				string fn = HttpContext.Current.Server.MapPath(filename);

		        using (StreamReader sr = new StreamReader(fn))
		        {
		            inn = sr.ReadToEnd();
		        }
			}
			catch
			{
				return (string.Empty);
			}

	        return (inn);
	    }

	    /// <summary>
	    /// Writes a string to a text file.
	    /// </summary>
		/// <example>
		/// <code>
		/// string result = Halide.Storage.WriteFile("/documents/file.txt", contentString);
		/// </code>
		/// </example>
	    /// <param name="filename">Web-formatted path and file name to save.</param>
	    /// <param name="content">String to save into file.</param>
		/// <returns>Empty String if successful, error message string on failure.</returns>
	    public static string WriteFile(string filename, string content)
	    {
			try
			{
				string fn = HttpContext.Current.Server.MapPath(filename);

		        if (File.Exists(fn)) File.Delete(fn);

		        StreamWriter sw = new StreamWriter(fn, false);

		        sw.Write(content);
		        sw.Close();

				return (string.Empty);

			}
			catch (Exception e)
			{
				return ("<pre>" + e + "</pre>");
			}

	    }

	    /// <summary>
	    /// Reads filenames from a directory and returns an ArrayList.
	    /// </summary>
		/// <example>
		/// <code>
		/// ArrayList filenames = Halide.Storage.GetFiles("/pdf", "*.pdf|*.pdfx", Halide.Storage.PathOptions.Path);
		/// </code>
		/// </example>
	    /// <param name="path">Web-formatted path to the files.</param>
	    /// <param name="filter">String filename wildcard (*.* is assumed by default).
		/// Separate multiple filespecs with a bar character (eg. "*.gif|*.jpg")</param>
	    /// <param name="options">options for returned files inclusion, et al.
		/// (eg. (PathOptions.Hidden | PathOptions.System | PathOptions.Path) returns all files,
		/// including hidden and system files, and the filename includes the full path).</param>
		/// <returns>ArrayList of filenames.</returns>
	    public static ArrayList GetFiles(string path, string filter, PathOptions options)
	    {
			ArrayList fileArray = new ArrayList();
			FileInfo objFI;
			string[] files;

			if(String.IsNullOrEmpty(filter)) filter = "*.*";

			string [] filespec = filter.Split('|');

			try
			{
				for(int L = 0; L < filespec.Length; L++)
				{
					files = System.IO.Directory.GetFiles(HttpContext.Current.Server.MapPath(path), filespec[L]);

					for(int i= 0;i< files.Length;i++)
					{
					    objFI = new System.IO.FileInfo(files[i]);

						if	(
								(objFI.Attributes.ToString().IndexOf("Hidden")<0 && objFI.Attributes.ToString().IndexOf("System")<0)
								||
								((int)(options & PathOptions.Hidden) != 0 && objFI.Attributes.ToString().IndexOf("Hidden") >= 0)
								||
								((int)(options & PathOptions.System) != 0 && objFI.Attributes.ToString().IndexOf("System") >= 0)
							)
						{
							if(!fileArray.Contains(objFI.Name) && !fileArray.Contains(objFI.FullName))
							{
								fileArray.Add( ((int)(options & PathOptions.Path) != 0) ? objFI.FullName : objFI.Name );
							}
						}
					}
				}
			}
			catch
			{}
			return fileArray;
		}

        /// <summary>
        /// Reads directory names from a gievn path and returns a string array.
        /// </summary>
        /// <example>
        /// <code>
        /// string[] folderNames = Halide.Storage.GetFolders("/pdf/");
        /// </code>
        /// </example>
        /// <param name="path">Web-formatted parent path to examine.</param>
        /// <returns>String array of folder names.</returns>
        public static string[] GetFolders(string path)
        {
            string[] folders = System.IO.Directory.GetDirectories(HttpContext.Current.Server.MapPath(path));

            for (int i = 0; i < folders.Length; i++)
                folders[i] = folders[i].Substring(folders[i].LastIndexOf("\\") + 1);

            return folders;
        }

	    /// <summary>
	    /// Reads filenames from a directory and returns an ArrayList.
	    /// </summary>
		/// <example>
		/// <code>
		/// ArrayList filenames = Halide.Storage.GetFiles("/pdf", "*.pdf|*.pdfx");
		/// </code>
		/// </example>
	    /// <param name="path">Web-formatted path to the files.</param>
	    /// <param name="filter">String filename wildcard (*.* is assumed by default).
		/// Separate multiple filespecs with a bar character (eg. "*.gif|*.jpg")</param>
		/// <returns>ArrayList of filenames.</returns>
	    public static ArrayList GetFiles(string path, string filter)
	    {
			return GetFiles(path, filter, 0);
		}

	    /// <summary>
	    /// Determines if a file exists at a given web path.
	    /// </summary>
		/// <example>
		/// <code>
		/// bool result = Halide.Storage.FileExists("/pdf/document.pdf");
		/// </code>
		/// </example>
	    /// <param name="path">Path to a file (eg. "/downloads/file.jpg").</param>
		/// <returns>true or false.</returns>
	    public static bool FileExists(string path)
	    {
			string filename = HttpContext.Current.Server.MapPath(path);
			return (System.IO.File.Exists(filename));
	    }

	    /// <summary>
	    /// Get the file size in bytes of a file on disk.
	    /// </summary>
		/// <example>
		/// <code>
		/// Long fileSize = Halide.Storage.GetFileSize("/pdf/document.pdf");
		/// </code>
		/// </example>
	    /// <param name="path">Path to a file (eg. "/downloads/file.jpg").</param>
	    /// <returns>File size in bytes.</returns>
	    public static long GetFileSize(string path)
	    {
	        long fileSize = 0;

	        if (FileExists(path))
	        {
	            string fileName = HttpContext.Current.Server.MapPath(path);
	            FileInfo fi = new FileInfo(fileName);
	            fileSize = fi.Length;
	        }
	        return fileSize;
	    }

	    /// <summary>
	    /// Determines if a directory exists at a given web path.
	    /// </summary>
		/// <example>
		/// <code>
		/// bool result = Halide.Storage.DirectoryExists("/pdf/");
		/// </code>
		/// </example>
	    /// <param name="path">Path to a directory (eg. "/downloads/").</param>
	    /// <returns>true or false.</returns>
	    public static bool DirectoryExists(string path)
	    {
	        string filePath = HttpContext.Current.Server.MapPath(path);
	        return (System.IO.Directory.Exists(filePath));
	    }

	    /// <summary>
	    /// Create a directory at a given web path.
	    /// </summary>
		/// <example>
		/// <code>
		/// bool result = Halide.Storage.CreateDirectory("/pdf");
		/// </code>
		/// </example>
	    /// <param name="path">Path and new directory (eg. "/downloads/newdir").</param>
	    /// <returns>true if created, false if not.</returns>
	    public static bool CreateDirectory(string path)
	    {
	        bool retVal = false;

	        if(!DirectoryExists(path))
	        {
	            string filePath = HttpContext.Current.Server.MapPath(path);
	            DirectoryInfo DO = System.IO.Directory.CreateDirectory(filePath);

	            retVal = DO.Exists;
	        }
	        return retVal;
	    }

	    /// <summary>
	    /// Deletes file(s) from a directory, based on age.
	    /// </summary>
		/// <example>
		/// <code>
		/// int deletedFilesCount = Halide.Storage.DeleteFiles("/pdf/", "*.tmp|*.chk", Halide.Storage.PathOptions.Path, DateTime.Now);
		/// </code>
		/// </example>
	    /// <param name="path">Web-formatted path to the files or a full path and filename to delete a single file (the later ignores any age params).</param>
	    /// <param name="filter">String filename wildcard (*.* is assumed by default). Separate multiple filespecs with a bar character (eg. "*.gif|*.jpg").</param>
	    /// <param name="options">options for file inclusion (PathOptions.Hidden | PathOptions.System) deletes all files, including hidden and system files.</param>
	    /// <param name="age">DateTime object; files older than this (creation date; within 1 minute) are deleted.</param>
		/// <returns>Number of files deleted successfully.</returns>
	    public static int DeleteFiles(string path, string filter, PathOptions options, System.DateTime age)
	    {
			FileInfo objFI;
			string[] files;
			int result = 0;

			if(String.IsNullOrEmpty(filter)) filter = "*.*";

			string [] filespec = filter.Split('|');

			if(FileExists(path))
			{
			    objFI = new System.IO.FileInfo(HttpContext.Current.Server.MapPath(path));
				objFI.Delete();
				result++;
			}
			else
			{

				try
				{
					for(int L = 0; L < filespec.Length; L++)
					{
						files = System.IO.Directory.GetFiles(HttpContext.Current.Server.MapPath(path), filespec[L]);

						for(int i= 0;i< files.Length;i++)
						{
							objFI = new System.IO.FileInfo(files[i]);

							if	(
									(objFI.Attributes.ToString().IndexOf("Hidden")<0 && objFI.Attributes.ToString().IndexOf("System")<0)
									||
									((int)(options & PathOptions.Hidden) != 0 && objFI.Attributes.ToString().IndexOf("Hidden") >= 0)
									||
									((int)(options & PathOptions.System) != 0 && objFI.Attributes.ToString().IndexOf("System") >= 0)
								)
							{
								if(Halide.Temporal.DateDiff(Halide.Temporal.DateDiffComparisonType.minutes, age, objFI.CreationTime) > 1)
								{
									objFI.Delete();
									result++;
								}
							}
						}
					}
				}
				catch
				{
				}
			}

			return result;
		}

		/// <summary>
		/// Deletes file(s) immediately.
		/// </summary>
		/// <example>
		/// <code>
		/// int deletedFilesCount = Halide.Storage.DeleteFiles("/pdf/", "*.tmp|*.chk", true);
		/// </code>
		/// </example>
		/// <param name="path">Web-formatted path where the files exist.</param>
	    /// <param name="fileSpec">String filename wildcard (*.* is assumed by default). Separate multiple filespecs with a bar character (eg. "*.gif|*.jpg").</param>
		/// <param name="includeSpecial">Set to true to deleted hidden/system files.</param>
		/// <returns>Number of files deleted successfully.</returns>
		public static int DeleteFiles(string path, string fileSpec, bool includeSpecial)
	    {
			if (includeSpecial)
			{
				return DeleteFiles(path, fileSpec, (PathOptions.Hidden | PathOptions.System), System.DateTime.Now);
			}
			else
			{
				return DeleteFiles(path, fileSpec, 0, System.DateTime.Now);
			}
	    }

		/// <summary>
		/// Deletes file(s) immediately.
		/// </summary>
		/// <example>
		/// <code>
		/// int deletedFilesCount = Halide.Storage.DeleteFiles("/pdf/", "*.tmp|*.chk");
		/// </code>
		/// </example>
		/// <param name="path">Web-formatted path where the files exist.</param>
	    /// <param name="fileSpec">String filename wildcard (*.* is assumed by default). Separate multiple filespecs with a bar character (eg. "*.gif|*.jpg").</param>
		/// <returns>Number of files deleted successfully.</returns>
		public static int DeleteFiles(string path, string fileSpec)
	    {
			return DeleteFiles(path, fileSpec, 0, System.DateTime.Now);
		}

		/// <summary>
		/// Deletes a single file immediately.
		/// </summary>
		/// <example>
		/// <code>
		/// int deletedFilesCount = Halide.Storage.DeleteFiles("/pdf/document.pdf");
		/// </code>
		/// </example>
		/// <param name="fileSpec">Web-formatted path with file name.</param>
		/// <returns>1 on success, 0 on failure</returns>
		public static int DeleteFiles(string fileSpec)
		{
			return DeleteFiles(fileSpec, "", 0, System.DateTime.Now);
		}

	    /// <summary>
	    /// Creates a unique, cryptic filename (based on GUID) for use in temporary file manipulation.
	    /// </summary>
		/// <example>
		/// <code>
		/// string filename = Halide.Storage.MakeUniqueFilename("jpg");
		/// </code>
		/// </example>
	    /// <param name="ext">File extension to append to the name, without leading period.</param>
	    /// <returns>Unique filename as string.</returns>
	    public static string MakeUniqueFilename(string ext)
	    {
	        string mvarFileName = string.Empty;

	        Guid guid = Guid.NewGuid();
	        mvarFileName = guid.ToString().Replace("-", "");
	        mvarFileName += "." + ext;

	        return mvarFileName;
	    }

	    /// <summary>
	    /// Retrieve a file's extension, without the period.
	    /// </summary>
		/// <example>
		/// <code>
		/// string fileExt = Halide.Storage.GetFileExtension(filename);
		/// </code>
		/// </example>
	    /// <param name="fileName">File name to parse.</param>
	    /// <returns>File extension as a string.</returns>
	    public static string GetFileExtension(string fileName)
	    {
	        string ext = string.Empty;

	        int x = fileName.LastIndexOf(".");

	        if (x > 0 && x < fileName.Length) ext = Halide.StringJuggler.Right(fileName, fileName.Length - x - 1);

	        return ext;
	    }

		/// <summary>
		/// Replaces any invalid characters ina file name with underscores,
		/// returning the filtered filename.
		/// </summary>
		/// <param name="fileName">File name to filter.</param>
		/// <returns>Filtered file name.</returns>
		public static string MakeValidFileName(string fileName)
		{
			return Regex.Replace(fileName, @"[^\s\w\.-]", "_");
		}

	// Outer Identify class
	}

// Outer namespace
}